#!/usr/bin/python
# coding=utf8
###############################################################################
#                                                                             #
#    Copyright 2007 Danil Dotsenko                                            #
#                                                                             #
#    This program is free software; you can redistribute it and/or modify     #
#    it under the terms of the GNU General Public License as published by     #
#    the Free Software Foundation, version 2 of the License.                  #
#                                                                             #
#    This program is distributed in the hope that it will be useful,          #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of           #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
#    GNU General Public License for more details.                             #
#                                                                             #
#    You should have received a copy of the GNU General Public License        #
#    along with this program; if not, write to the Free Software              #
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA #
#                                                                             #
###############################################################################                                   #
# Contacts: Danil Dotsenko, dd@accentsolution.com

from elementclasses import *
import time

def meterClicked(widget, meter, button):
	cfgobj = getcfg()  # this is a pointer to an {} object.
	try:
		lastclick = float(cfgobj['lastclick'])
	except:
		lastclick = 0.0
	if lastclick < time.time():
		cfgobj['lastclick'] = time.time()+0.5
		#try:
		cfgobj['clickables'][meter].clicked(button)
		#except:
		#	pass

def widgetClicked(widget, x, y, button):
	'''
	We are using this function to catch mouse wheels up and down.
	The latest (0.42 (kde3.5.7) ) version of SuperKaramba has meterClicked support for buttons 4 and 5
	but in the spirit of backward support we are still catching this over widgetClicked.
	WidgetClicked does not tell which meter is clicked, so we have to calculate that by hand.
	
	(the mouse wheel support through meterClicked has to be enabled manually. 
	 So, do not be affraid about double-scroll effect though both, widgetClicked and meterClicked
	 reporting the same scroll event.
	 
	 If you active the mouse wheel detection for meterClicked, handle double scroll problem yourself.)
	
	Our goal is to detect the (a) Top-most, (b) visible object.
	Because in SK the top object = object created last, the magic it to find which of the
	objects that are visible and are under the mouse have the latest time stamp.
	
	SK objects do not have timestamp, so we assign the timestamp at object's creation time and check for it now.
	'''
	if button in [4,5]:
		cfgobj = getcfg('clickables')
		obj = None
		for i in cfgobj:
			try:
				visible = cfgobj[i].visible
			except:
				visible = False
			if visible:
				try:
					px1,py1 = cfgobj[i].position() # this is upper left corner
					px2,py2 = cfgobj[i].size()
					px2,py2 = px1+px2,py1+py2 # above we get pure size. Here we adjust that to lower right corner
				except:
					px1,py1,px2,py2 = -1, -1, -1, -1 # this will be out of range for any Clicked x y. Happens if non-object
				if (px1 <= x <= px2) and (py1 <= y <= py2):
					try:
						selected = obj.timestamp
					except:
						selected = 0
					try:
						new = cfgobj[i].timestamp
					except:
						new = 0
					if new > selected: # i.e. it is newer, and is higher in Z order.
						obj = cfgobj[i]
		#try:
		obj.clicked(button)
		#except:
		#	pass

def keyPressed(widget, meter, char):
	cfgobj = getcfg()
	if not char:
		# probably one the ctrl, alt, win, shift keys.
		cfgobj['metabutton'] = True
		pass
	elif meter:
		if char == chr(13): # enter
			try:
				if not cfgobj['submited']: # here we are guarding from rapidly repeating Enter presses.
					cfgobj['submited'] = True
					if cfgobj['metabutton']: # this simulates entering \n with CTRL+ENTER
						cfgobj['clickables'][meter].text(cfgobj['clickables'][meter].text()+'\n')
					else:
						cfgobj['clickables'][meter].submit()
			except:
				pass
			cfgobj['metabutton'] = False
		elif char == chr(27): # esc
			cfgobj['submited'] = False
			cfgobj['metabutton'] = False
			try:
				cfgobj['clickables'][meter].escape()
			except:
				pass
		else:
			cfgobj['submited'] = False
			cfgobj['metabutton'] = False
			try:
				cfgobj['clickables'][meter].clicked(char)
			except:
				pass

def itemDropped(widget, dropText, x=0, y=0):
	# let's pass the info only to registered objects.
	cfgobj = getcfg('dropables')
	for i in cfgobj:
		try:
			w,h = cfgobj[i].size()
			px,py = cfgobj[i].position()
		except:
			w,h,px,py = -1, -1, -1, -1
		if (py <= y <= h):
			if (px <= x <= w): # the reason for nesting if's is simple. if one fails, we save cycles. Y fails more often.
				#try:
				cfgobj[i].itemDropped(dropText, x, y)
				#except:
				#	pass

def widgetMouseMoved(widget, x, y, button):
	cfg = getcfg('hoverables') # dict - {obj.meter:obj}
	stack = getcfg('hoverstack') # array - [obj]
	alreadybusy = getcfg('hoverbusy') # array with only one item. Used to guard against threads.
	if cfg and not alreadybusy[0]:
		alreadybusy[0] = True
		newstack = []
		for item in cfg.keys():
			x1,y1,x2,y2 = cfg[item].position()+cfg[item].size() # now, it's (x,y,w,h)
			y2 = y2+y1
			# now it's upper left and lower right corners.
			if y1 < y < y2:
				# most of widgets are wide. Testing for Y 1st will save cycles.
				x2 = x2+x1
				if x1 < x < x2 and cfg[item].visible:
					newstack.append(cfg[item])
		newstack.sort()
		stack.sort()
		if newstack != stack:
			mouseout = []
			for item in stack.__getslice__(0,stack.__len__()): # this is an equivalnt of obj.copy() from dicttypes.
				try:
					i = newstack.index(item) # if no item, this will fail and run Except part.
					# if this works - we are hovering above the same items.
					# do nothing or, maybe, renew the auto-mouse-out counter thread.
					newstack.remove(item) # REMAINING ITEMS WILL BE MOUSE-IN and added to stack in the end.
				except:
					stack.remove(item)
					mouseout.append(item)
			# now we have:
			# newstack - a list of NEWLY hovered-onto objects.
			#	we need to emit a mousein() signal on all of them.
			# stack - an updated list of obj above which we continue to hover from last time.
			#	we do nothing here, except, maybe update some auto-mouse-out timer
			#	because SK doesn't have the mouseout and we have to simulate it sometimes.
			# mouseout = a list of obj from which we moved out.
			#	we need to emit a mouseout() signal on all of them.
			# roll the new items into the stack list.
			for item in mouseout:
				item.mouseout(x,y,button)
			for item in newstack:
				item.mousein(x,y,button)
			stack.extend(newstack)
	alreadybusy[0] = False